﻿/**
 * Canvas thể hiện Menu chính của game
 * Menu gồm các menu item, thể hiện bởi các dòng text
 * Nền menu là các quả bong bóng với kích thước khác nhau nổi lên
 * với vận tốc ngẫu nhiên, mỗi quả bóng là 1 BubbleSprite
 */

import java.util.Enumeration;
import java.util.Vector;

import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.game.GameCanvas;
import javax.microedition.lcdui.game.LayerManager;

public class MenuCanvas extends GameCanvas implements Runnable {
	
	private static final int BGR_COLOR = 0x3A79FF;
	private static final int PAN_DELAY = 50;
	private static final int NUM_BUBBLES = 15;
	
	/**
	 * Chiều rộng của Canvas này
	 */
	private int width;
	/**
	 * Chiều cao của Canvas này
	 */
	private int height;
	
	private BubbleSprite[] bubbles;
	
	/**
	 * Do tại mỗi thời điểm Menu sẽ hiện thị các menu item khác nhau
	 * Ví dụ: ban đầu là menu gốc, với các item: New Game, High Score, ...
	 * Nhưng khi user chọn New Game thì lại hiện lên thay thế bởi
	 * các item khác thuộc nhánh item New Game là: Lines, Squares, Block, ...
	 * Nên ta cần 1 vector lưu trữ danh sách các item cần hiển thị ở
	 * thời điểm hiện tại
	 * Thead chính của ta chỉ việc truy xuất các phần tử trong vector này
	 * và theo đó vẽ lên canvas
	 * Mỗi phần tử của vector là thuộc kiểu ItemMenu
	 * Xem thêm lớp ItemMenu
	 */
	private Vector curItems;
	
	/**
	 * Khi người chơi đang ở Item nào, item đó sẽ được tô màu nền
	 * Field này lưu trữ chỉ số của item đang được chọn hiện tại
	 * Tất nhiên 0 <= highlight <= curItems.size() - 1
	 */
	public int highlight;
	
	/**
	 * Lưu lại vị trí item được chọn trước đó ở Menu gốc
	 * Ví dụ khi ta vào item Option, rồi chọn Back để quay lại Menu gốc
	 * thì ta cần phải highlight item Option lại
	 * Ban đầu sẽ là item đầu tiên, chỉ số 0
	 */
	private int saveHighlight = 0;
	
	private LayerManager layerManager;
	
	private Thread thread;
	
	private Display display;
	
	/**
	 * Tham chiếu đến Midlet chính để gọi exit() khi user chọn item Exit
	 */
	private AppMidlet app;
	
	/**
	 * Canvas thể hiện bảng chơi game, hiện khi user chọn
	 * item Lines hoặc Squares hoặc Blocks trong nhánh New Game
	 */
	private BoardCanvas board;
	
	/**
	 * Trạng thái game hiện tại, static để có thể truy xuất nhanh từ Midlet
	 * phục vụ cho việc lưu lại trạng thái trước khi thoát
	 */
	public static GameShape gs;
	
	/**
	 * Canvas giới thiệu, hiện khi user chọn item About
	 */
	private AboutCanvas about;
	
	/**
	 * Tham chiếu phục vụ cho việc play các file âm thanh
	 */
	private MediaPlayer media;
	
	/**
	 * Canvas Bảng xếp hạng, hiện khi user chọn item High Score
	 */
	private HighScoreCanvas highScore;
	
	public MenuCanvas(Display display, AppMidlet app) {
		super(false);
		this.display = display;
		this.app = app;
		setFullScreenMode(true);
		
		width = getWidth();
		height = getHeight();
		
		layerManager = new LayerManager();
		gs = MyIO.loadGameShape();
		setupBubbles();
		showRootMenu();
		
		media = new MediaPlayer();
		highScore = new HighScoreCanvas(display, this);
		/*
		 * Cả board và highScore đều cần tham chiếu đến lẫn nhau
		 * Do trong board, khi game over thì hiện highScore
		 * Và trong highScore, khi nhấn OK có thể quay về board và
		 * gọi các hàm reset lại board để chơi lượt mới
		 */
		board = new BoardCanvas(display, this, media, highScore);
		highScore.board = board;
		about = new AboutCanvas(display, this);
	}

	public void showRootMenu() {
		curItems = buildRootMenu();
		highlight = saveHighlight;
	}
	
	private void showOptionMenu() {
		curItems = buildOptionMenu();
		saveHighlight = highlight;
		highlight = 0;
	}
	
	private void showNewGameMenu() {
		curItems = buildNewGameMenu();
		saveHighlight = highlight;
		highlight = 0;
	}
	
	private void showAbout() {
		display.setCurrent(about);
	}
	
	private void setupBubbles() {
		bubbles = new BubbleSprite[NUM_BUBBLES];
		for (int i = 0; i < NUM_BUBBLES; i++) {
			/*
			 * Các bubbles cần biết width và height của Canvas hiển thị chúng,
			 * Để có thể xác định tọa độ ban đầu của chúng hợp lý
			 * Xem thêm lớp BubbleSprite
			 */
			bubbles[i] = new BubbleSprite(10 + i, width, height);
			layerManager.append(bubbles[i]);
		}
	}

	/**
	 * Các quả bubbles nổi lên 1 chút
	 */
	private void updateBubbles() {
		for (int i = 0; i < NUM_BUBBLES; i++) {
			bubbles[i].moveUp(i % 4 + 1);
		}
	}
	
	/**
	 * Reset thuộc tính của các bubbles để xuất hiện hiệu ứng
	 * xổ menu dần dần từ trên xuống
	 * Xem thêm hàm reset() của lớp ItemMenu
	 */
	public void resetForAnimate() {
		if (curItems == null) return;
		Enumeration e = curItems.elements();
		while (e.hasMoreElements()) {
			ItemMenu item = (ItemMenu) e.nextElement();
			item.reset();
		}
	}
	
	/**
	 * Vẽ tất cả các item hiện tại lên canvas
	 * @param g truyền vào cho hàm paint của mỗi item
	 * Xem thêm hàm paint của lớp ItemMenu
	 */
	private void paintItems(Graphics g) {
		if (curItems == null) return;
		Enumeration e = curItems.elements();
		int i = 0;
		while (e.hasMoreElements()) {
			ItemMenu item = (ItemMenu) e.nextElement();
			/*
			 * i == hightlight để xem item này sẽ được tô nền hay không
			 */
			item.paint(g, i == highlight);
			i++;
		}
	}
	
	private Vector buildRootMenu() {
		Vector vt = new Vector();
		int number = 5;
		int index = 0;
		/*
		 * Nếu có trạng thái game được lưu trước đó thì mới hiện item Continue
		 */
		if (gs != null) {
			vt.addElement(new ItemMenu("CONTINUE",	 0, index++, 6, width, height));
			number = 6;
		}
		
		vt.addElement(new ItemMenu("NEW GAME",	 1, index++, number, width, height));
		vt.addElement(new ItemMenu("HIGH SCORE", 2, index++, number, width, height));
		vt.addElement(new ItemMenu("OPTION",	 3, index++, number, width, height));
		vt.addElement(new ItemMenu("ABOUT",		 4, index++, number, width, height));
		vt.addElement(new ItemMenu("EXIT GAME",	 5, index++, number, width, height));
		
		return vt;
	}
	
	private Vector buildOptionMenu() {
		String sound = Option.isSound ? "ON" : "OFF";
		String next = Option.isShowNext ? "ON" : "OFF";
		Vector vt = new Vector();
		vt.addElement(new ItemMenu("SOUND: " + sound,	 30, 0, 4, width, height));
		vt.addElement(new ItemMenu("SHOW NEXT: " + next, 31, 1, 4, width, height));
		vt.addElement(new ItemMenu("", 					 -1, 2, 4, width, height));
		vt.addElement(new ItemMenu("BACK",				 33, 3, 4, width, height));
		
		return vt;
	}
	
	private Vector buildNewGameMenu() {
		Vector vt = new Vector();
		for (int i = 0; i < 3; i++) {
			vt.addElement(new ItemMenu(
					BoardCanvas.GAME_TYPE[i], 10 + i, i, 5, width, height));
		}
		vt.addElement(new ItemMenu("",		 	 -1, 3, 5, width, height));
		vt.addElement(new ItemMenu("BACK",	 	 14, 4, 5, width, height));
		
		return vt;
	}
	
	protected void showNotify() {
		thread = new Thread(this);
		thread.start();
		if (Option.isSound) {
			media.loopMenu();
		}
	}
	
	protected void hideNotify() {
		thread = null;
		/*
		 * Tắt nhạc nền
		 */
		media.stopMenu();
	}
	
	public void run() {
		Graphics g = getGraphics();
		Thread t = Thread.currentThread();
		try {
			while (t == thread) {
				updateBubbles();
				render(g);
				Thread.sleep(PAN_DELAY);
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
	
	private void render(Graphics g) {
		g.setColor(BGR_COLOR);
		g.fillRect(0, 0, width, height);
		layerManager.paint(g, 0, 0);
		paintItems(g);
		flushGraphics();
	}
	
	protected void pointerPressed(int x, int y) {
		/*
		 * Xác định item được chọn
		 */
		int totalHeight = ItemMenu.ITEM_HEIGHT * curItems.size();
		int firstY = (height - totalHeight) / 2;
		if (y < firstY || y > firstY + totalHeight)
			return;
		int index = (y - firstY) / ItemMenu.ITEM_HEIGHT;
		if (((ItemMenu) curItems.elementAt(index)).eventCode == -1)
			return;
		/*
		 * Đã xác định item được chọn
		 */
		highlight = index;
		/*
		 * Đi vào item đó, làm như khi user nhấn fire
		 */
		fireAction();
	}
	
	/**
	 * Chỉ khi lên xuống, tô lại item được highlight
	 */
	protected void keyRepeated(int keyCode) {
		int action = getGameAction(keyCode);
		switch (action) {
		case Canvas.UP:
		case Canvas.DOWN:
			keyPressed(keyCode);
			return;
		}
	}
	
	protected void keyPressed(int keyCode) {
		int action = getGameAction(keyCode);
		switch (action) {
		case Canvas.UP:
			if (curItems != null) {
				/*
				 * Highlight item kề trên
				 */
				decHighlight();
			}
			break;
		case Canvas.DOWN:
			if (curItems != null) {
				/*
				 * Highlight item kề dưới
				 */
				incHighlight();
			}
			break;
		case Canvas.FIRE:
			/*
			 * Đi vào item được chọn
			 */
			fireAction();
		}
	}

	private void fireAction() {
		if (curItems == null) return;
		ItemMenu item = getHighlightItem(); // Xác định đối tượng item được chọn
		/*
		 * Dựa vào mã sự kiện (eventCode) của đối tượng được chọn mà
		 * có cách hành xử thích hợp
		 */
		switch (item.eventCode) {
		case 0: // CONTINUE
			goToBoard();
			break;
		case 1:
			showNewGameMenu();
			break;
		case 2:
			showHighScore();
			break;
		case 3:
			showOptionMenu();
			break;
		case 4:
			showAbout();
			break;
		case 5: // EXIT GAME
			app.exit();
			break;
		case 10: case 11: case 12: // các loại new game
			/*
			 * gs trỏ vào 1 đối tượng GameShape mới ngẫu nhiên, score = 0
			 */
			gs = new GameShape(Algorithms.createNewMatrix(), 0,
					(byte) (item.eventCode - 10));
			/*
			 * Sau đó rồi cũng vào board để user chơi
			 */
			goToBoard();
			break;
		case 30: // TOGGLE SOUND: ON/OFF
			Option.isSound = !Option.isSound;
			item.text = "SOUND: " + (Option.isSound ? "ON" : "OFF");
			if (Option.isSound) {
				media.loopMenu();
			} else {
				media.stopMenu();
			}
			break;
		case 31: // TOGGLE SHOW NEXT: ON/OFF
			Option.isShowNext = !Option.isShowNext;
			item.text = "SHOW NEXT: " + (Option.isShowNext ? "ON" : "OFF");
			break;
		case 14: // BACK FROM: New Game
		case 33: // BACK FROM: Option
			showRootMenu();
		}
	}

	private void showHighScore() {
		display.setCurrent(highScore);
	}

	private ItemMenu getHighlightItem() {
		return (ItemMenu) curItems.elementAt(highlight);
	}
	
	private void incHighlight() {
		/*
		 * Nếu đang là item cuối cùng thì về đầu tiên
		 */
		highlight = highlight == curItems.size() - 1 ? 0 : highlight + 1;
		if (getHighlightItem().eventCode == -1) {
			incHighlight();
		}
	}
	
	private void decHighlight() {
		/*
		 * Nếu đang là item đầu tiên thì đến cuối cùng
		 */
		highlight = highlight == 0 ? curItems.size() - 1 : highlight - 1;
		if (getHighlightItem().eventCode == -1) {
			decHighlight();
		}
	}
	
	/**
	 * Vào chơi game
	 */
	private void goToBoard() {
		board.setGameShape(gs); // Đặt lại trạng thái game mới
		display.setCurrent(board);
	}

}
